﻿using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Web;
using HtmlAgilityPack;
using Joel.Net;
using Microsoft.SharePoint;
using CKS.EBE.API;

namespace CKS.EBE.Linkbacks
{
    static class Helper
    {
        /// <summary>
        /// the linking post needs to have a link to us. if not, we are considering the request as spam.
        /// also checks akismet if an akismet key is found in the blog settings
        /// </summary>
        /// <param name="localPostUrl">e.g. http://www.hezser.de/Lists/Posts/ViewPost.aspx?ID=1</param>
        /// <param name="linkingPostUrl">e.g. http://renehezser.wordpress.com/2009/06/13/testtrackback/</param>
        /// <param name="localPostId">1</param>
        /// <param name="request"></param>
        /// <param name="akismetApproved"></param>
        /// <param name="linkingPostTitle">used for akismet check. the linking title will be used if not passed</param>
        /// <returns></returns>
        internal static bool DetectSpam(string localPostUrl, string linkingPostUrl, int localPostId, HttpRequest request, out bool akismetApproved, ref string linkingPostTitle)
        {
            bool spamDetected = false;
            akismetApproved = false;
            try
            {
                if (Uri.IsWellFormedUriString(linkingPostUrl, UriKind.Absolute) == false)
                {
                    spamDetected = true;
                }
                else
                {
                    // Attempt to fetch URL ("HEAD" request)
                    var webRequest = (HttpWebRequest) WebRequest.Create(linkingPostUrl);
                    webRequest.Method = "GET";
                    webRequest.MaximumAutomaticRedirections = 4;
                    webRequest.Timeout = 20000;
                    webRequest.ServicePoint.MaxIdleTime = 20000;
                    var webResponse = (HttpWebResponse) webRequest.GetResponse();
                    if (webRequest.HaveResponse == false) return true;
                    
                    var statusCode = (int) (webResponse.StatusCode);
                    if (statusCode < 200 || statusCode >= 300) return true;

                    using (var webstream = new StreamReader(webResponse.GetResponseStream()))
                    {
                        var htmlDoc = new HtmlDocument();
                        htmlDoc.Load(webstream);
                        string outerHtml = htmlDoc.DocumentNode.OuterHtml;
                        int indexOf = outerHtml.IndexOf(localPostUrl, StringComparison.OrdinalIgnoreCase);
                        if (indexOf == -1)
                        {
                            //also check for local post url with rewritten name
                            // e.g. http://www.hezser.de/archive/2009/04/23/welcome-to-your-blog.aspx
                            using (var site = new SPSite(localPostUrl))
                            using (var web = site.OpenWeb())
                            {
                                var list = EBE.Helper.GetListByFolderName(web, Localization.GetListName(Localization.BlogList.Posts, web.Language));
                                SPListItem postItem = list.GetItemById(localPostId);
                                localPostUrl = web.Url + XslMethods.createPostUrl(((DateTime)postItem["Created"]).ToString("yyyy-MM-dd HH:mm"), (string)postItem["BlogTitleForUrl"], postItem.ID.ToString());
                                indexOf = outerHtml.IndexOf(localPostUrl, StringComparison.OrdinalIgnoreCase);
                            }
                        }
                        if (indexOf != -1)
                        {
                            // get linking post title
                            HtmlNode title = htmlDoc.DocumentNode.SelectSingleNode(@"//title");
                            if (title != null && string.IsNullOrEmpty(linkingPostTitle))
                            {
                                // remove linebreaks and whitespaces from the beginning and end of the title
                                linkingPostTitle = title.InnerText.Trim(new[] {'\r', '\n', '\t', ' '});
                                linkingPostTitle = HttpUtility.HtmlDecode(linkingPostTitle);
                            }
                        }
                        else
                        {
                            spamDetected = true;
                        }
                    }
                    spamDetected = AkismetCheck(spamDetected, request, linkingPostTitle, linkingPostUrl, out akismetApproved);
                }
            }
            catch (Exception ex)
            {
                Trace.Write("Error Detecting Spam: " + ex);
                spamDetected = true;
            }
            return spamDetected;
        }

        /// <summary>
        /// check for spam with akismet, if a key is stored in the blogsettings
        /// </summary>
        /// <param name="spamDetected"></param>
        /// <param name="request"></param>
        /// <param name="linkingBlogName"></param>
        /// <param name="linkingPostUrl"></param>
        /// <param name="akismetApproved"></param>
        /// <returns></returns>
        private static bool AkismetCheck(bool spamDetected, HttpRequest request, string linkingBlogName, string linkingPostUrl, out bool akismetApproved)
        {
            akismetApproved = false;
            try
            {
                // Try Akismet test:
                string akismetKey = BlogContext.Current.Settings.AkismetKey;
                if (!spamDetected && !string.IsNullOrEmpty(akismetKey))
                {
                    akismetApproved = true;
                    string currentWebUrl = SPContext.Current.Web.Url;
                    var api = new Akismet(akismetKey, currentWebUrl, request.UserAgent);
                    if (api.VerifyKey())
                    {
                        var c = new AkismetComment
                        {
                            Blog = currentWebUrl,
                            UserIp = request.UserHostAddress,
                            UserAgent = request.UserAgent,
                            Referrer = Convert.ToString(request.UrlReferrer),
                            CommentContent = "",
                            CommentType = "pingback",
                            CommentAuthor = linkingBlogName,
                            CommentAuthorUrl = linkingPostUrl
                        };
                        spamDetected = api.CommentCheck(c);
                    }
                }
            }
            catch (Exception ex)
            {
                Trace.Write("Error checking for spam with Akismet: " + ex);
            }
            return spamDetected;
        }

        internal static string XmlEscape(string text)
        {
            return string.IsNullOrEmpty(text) ? string.Empty : HttpUtility.HtmlEncode(text);
        }
    }
}
